#include "functions.h"
#include "Utilities.h"
#include "operators.h"
#include "Number.h"
#include "Sym.h"
#include "Expr.h"
namespace sympycpp {
Expr Sin(const Expr & arg) {
	Ex * e = arg.innerCopy();
	Ex * a = Sin_(e, STEALING);
	return Expr(a, Expr::STEALING);
}
Ex * Sin_(Ex * &  e, const allocationPolicy) {
	bool done = false;
	Utilities::set0_2Pi(e, done); //detect and trensform to interval <0,2pi)
	if (done) { //is argument is the interval?
		if (e->isNum() && ((Number *)e)->eq(0)) { // sin(0) --> 0
			Ex * zero = e;
			e = 0;
			return zero;
		}
		if (e->str() == pi.str()) { // sin(pi) --> 0
			Ex * zero = new Number(0);
			delete e;
			e = 0;
			return zero;
		}
		if (e->str() == (pi/6).str() || e->str() == (5*pi/6).str()) { // sin(pi/6) == sin(5pi/6) --> 1/2
			Ex * two = new Number(2);
			Ex * minOne = new Number(-1);
			Ex * oneHalf = Operations::power(two,minOne,STEALING);
			delete e;
			e = 0;
			return oneHalf;
		}
		if (e->str() == (pi/2).str())  { // sin(pi/2) --> 1
			Ex * one = new Number(1);
			delete e;
			e = 0;
			return one;
		}
		if (e->str() == (3*pi/2).str())  { // sin(3pi/2) --> -1
			Ex * minOne = new Number(-1);
			delete e;
			e = 0;
			return minOne;
		}
		if (e->str() == (7*pi/6).str() || e->str() == (11*pi/6).str()) { // sin(7pi/6) == sin(11pi/6) --> -1/2
			Ex * two = new Number(2);
			Ex * minOne = new Number(-1);
			Ex * minOneHalf = Operations::power(two,minOne,STEALING);
			minOneHalf->sign(minus);
			delete e;
			e = 0;
			return minOneHalf;
		}
	}
	return new sinus_(e, STEALING);
}
Expr Cos(const Expr & arg) {
	Ex * e = arg.innerCopy();
	Ex * a = Cos_(e, STEALING);
	return Expr(a, Expr::STEALING);
}
Ex * Cos_(Ex * & e, const allocationPolicy) {
	bool done = false;
	Utilities::set0_2Pi(e, done); // detect and trensform to interval <0,2pi)
	if (done) { // is argument in the interval?
		if (e->isNum() && ((Number *)e)->eq(0)) { // cos(0) --> 1
			Number * one = (Number *)e;
			one->setValue(1);
			e = 0;
			return one;
		}
		if (e->str() == (pi/2).str() || e->str() == (3*pi/2).str())  { // cos(pi/2) == cos(3pi/2) --> 0
			Ex * zero = new Number(0);
			delete e;
			e = 0;
			return zero;
		}
		if (e->str() == (pi/3).str() || e->str() == (5*pi/3).str()) { // cos(pi/3) == cos(5pi/3) --> 1/2
			Ex * two = new Number(2);
			Ex * minOne = new Number(-1);
			Ex * oneHalf = Operations::power(two,minOne,STEALING);
			delete e;
			e = 0;
			return oneHalf;
		}
		if (e->str() == pi.str())  { // cos(pi) --> -1
			Ex * minOne = new Number(-1);
			delete e;
			e = 0;
			return minOne;
		}
		if (e->str() == (2*pi/3).str() || e->str() == (4*pi/3).str()) { // cos(2pi/3) == cos(4pi/3) --> -1/2
			Ex * two = new Number(2);
			Ex * minOne = new Number(-1);
			Ex * minOneHalf = Operations::power(two,minOne,STEALING);
			minOneHalf->sign(minus);
			delete e;
			e = 0;
			return minOneHalf;
		}
	}
	return  new cosinus_(e, STEALING);
}
Expr Tg(const Expr & e) {
	Ex * p = new tangent_(e);
	return Expr(p, Expr::STEALING);
}
Ex * Tg_(Ex * & e, const allocationPolicy) {
	Ex * p = new tangent_(e, STEALING);
	return p;
}
Expr Cotg(const Expr & e) {
	Ex * p = new cotangent_(e);
	return Expr(p, Expr::STEALING);
}
Ex * Cotg_(Ex * & e, const allocationPolicy) {
	Ex * p = new cotangent_(e, STEALING);
	return p;
}
Expr Ln(const Expr & e) {
	Ex * p = e.innerCopy();
	Ex * a = Ln_(p, STEALING);
	return Expr(a, Expr::STEALING);
}
Ex * Ln_(Ex * & e, const allocationPolicy) {
	if ( e->isNum() && ((Number *)e)->eq(1)) {
		return new Number(0);
	}
	else {
		return  new ln_(e, STEALING);
	}
}
Ex * sinus_::copy() const {
	Ex * f = new sinus_(e_);
	f->sign(sign());
	return f;
}
Ex * sinus_::diff(const Sym & x) const {
	Ex  * p1, * p2, * p3;
	p1 = new cosinus_(e_);
	p2 = e_->diff(x);
	p1->sign(sign());
	p3 = Operations::multiplication( p1, p2, STEALING);
	return p3;
}
Ex * sinus_::create(Ex * & arg, const allocationPolicy) const {
		return Sin_(arg, STEALING);
}
Ex * cosinus_::diff(const Sym & x) const {
	Ex * p1, * p2, * p3;
	p1 = new sinus_(e_);
	p2 = e_->diff(x);
	p1->sign(!sign());
	p3 = Operations::multiplication( p1, p2, STEALING);
	return p3;
}
Ex * cosinus_::copy() const {
        Ex * f = new cosinus_(e_);
	f->sign(sign());
	return f;
}
Ex * cosinus_::create(Ex * & arg, const allocationPolicy) const {
	return  Cos_(arg, STEALING);
}
Ex * tangent_::diff(const Sym & x) const {
	Ex * p1, * p2, * p3;
	p1 = new cosinus_(e_);
	p2 = new Number(-2);
	p3 = Operations::power( p1, p2, STEALING);
	p3->sign(sign()); 
	p2 = e_->diff(x);
	p1 = Operations::multiplication( p3, p2, STEALING);
	return p1;
}
Ex* tangent_::copy()const{
        Ex* f = new tangent_(e_);
	f->sign(sign());
	return f;
}
Ex * tangent_::create(Ex * & arg, const allocationPolicy) const {
	return Tg_(arg, STEALING);
}
Ex * cotangent_::diff(const Sym & x) const {
	Ex * p1, * p2, * p3;
	p1 = new sinus_(e_);
	p2 = new Number(-2);
	p3 = Operations::power( p1, p2, STEALING);
	p3->sign(sign());
	p2 = e_->diff(x);
	p1 = Operations::multiplication( p3, p2, STEALING);
	return p1;
}

Ex * cotangent_::copy() const {
        Ex * f = new cotangent_(e_);
	f->sign(sign());
	return f;
}
Ex * cotangent_::create(Ex * & arg, const allocationPolicy) const {
	return Cotg_(arg, STEALING);
}
Ex * ln_::diff(const Sym & x) const {
	Ex * p1, * p2, * p3;
	Number in(-1);
	p1 = Operations::power(e_, &in);
	p1->sign(sign());
	p2 = e_->diff(x);
	p3 = Operations::multiplication( p1, p2, STEALING);
	return p3;
}

Ex * ln_::copy() const {
        Ex * f = new ln_(e_);
	f->sign(sign());
	return f;
}
Ex * ln_::create(Ex * & arg, const allocationPolicy) const {
	return Ln_(arg, STEALING);
}
}
